<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="FfIaK" id="FfIaK"><span data-lake-id="udce0830d" id="udce0830d">典型回答</span></h1>
  <p data-lake-id="u22df6034" id="u22df6034"><br></p>
  <p data-lake-id="u9c127132" id="u9c127132"><span data-lake-id="u5e705814" id="u5e705814">语法糖（Syntactic sugar），指在计算机语言中添加的某种语法，这种语法对语言的功能并没有影响，但是更方便程序员使用。</span></p>
  <p data-lake-id="u75030a85" id="u75030a85"><span data-lake-id="ubb49ed65" id="ubb49ed65">​</span><br></p>
  <p data-lake-id="u10fa6375" id="u10fa6375"><span data-lake-id="u3f7418b9" id="u3f7418b9">虽然Java中有很多语法糖，但是Java虚拟机并不支持这些语法糖，所以这些语法糖在编译阶段就会被还原成简单的基础语法结构，这样才能被虚拟机识别，这个过程就是解语法糖。</span></p>
  <p data-lake-id="u43954abc" id="u43954abc"><span data-lake-id="u1dab1cf7" id="u1dab1cf7">​</span><br></p>
  <p data-lake-id="u7bf4eb53" id="u7bf4eb53"><span data-lake-id="u67643bc5" id="u67643bc5">如果看过Java虚拟机的源码，就会发现在编译过程中有一个重要的步骤就是调用desugar()，这个方法就是负责解语法糖的实现。</span></p>
  <p data-lake-id="uef689c55" id="uef689c55"><span data-lake-id="u03f5fd8e" id="u03f5fd8e">​</span><br></p>
  <p data-lake-id="udde55aa8" id="udde55aa8"><span data-lake-id="u6adcf9ed" id="u6adcf9ed">常见的语法糖有 switch支持枚举及字符串、泛型、条件编译、断言、可变参数、自动装箱/拆箱、枚举、内部类、增强for循环、try-with-resources语句、lambda表达式等。</span></p>
  <p data-lake-id="ubbceca9b" id="ubbceca9b"><span data-lake-id="u3f7ab205" id="u3f7ab205">​</span><br></p>
  <h1 data-lake-id="uTxHL" id="uTxHL"><span data-lake-id="u7b8e5c0d" id="u7b8e5c0d">知识扩展</span></h1>
  <p data-lake-id="u54ae19c2" id="u54ae19c2"><br></p>
  <h3 data-lake-id="VRPvm" id="VRPvm"><span data-lake-id="u679f152a" id="u679f152a">如何解语法糖？</span></h3>
  <p data-lake-id="u21de2dff" id="u21de2dff"><br></p>
  <p data-lake-id="u35b2ebbc" id="u35b2ebbc"><span data-lake-id="u8e9b093a" id="u8e9b093a">语法糖的存在主要是方便开发人员使用。但其实，Java虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构，这个过程就是解语法糖。</span></p>
  <p data-lake-id="uea648275" id="uea648275"><span data-lake-id="uc8cc5f40" id="uc8cc5f40">​</span><br></p>
  <p data-lake-id="u56619438" id="u56619438"><span data-lake-id="u1560240c" id="u1560240c">说到编译，大家肯定都知道，Java语言中javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。如果你去看com.sun.tools.javac.main.JavaCompiler的源码，你会发现在compile()中有一个步骤就是调用`desugar()`，这个方法就是负责解语法糖的实现的。</span></p>
  <p data-lake-id="ue745a130" id="ue745a130"><span data-lake-id="u602113a0" id="u602113a0">​</span><br></p>
  <p data-lake-id="u921c3d86" id="u921c3d86"><br></p>
  <h3 data-lake-id="be1c0fbd" id="be1c0fbd"><span data-lake-id="ubba2e5f1" id="ubba2e5f1">糖块一、 switch 支持 String 与枚举</span></h3>
  <p data-lake-id="u626952dc" id="u626952dc"><br></p>
  <p data-lake-id="u1a5e4d76" id="u1a5e4d76"><span data-lake-id="u0db11279" id="u0db11279">前面提到过，从Java 7 开始，Java语言中的语法糖在逐渐丰富，其中一个比较重要的就是Java 7中</span><code data-lake-id="u3070478d" id="u3070478d"><span data-lake-id="u7fd536cf" id="u7fd536cf">switch</span></code><span data-lake-id="ufeff0d44" id="ufeff0d44">开始支持</span><code data-lake-id="ud177e9ba" id="ud177e9ba"><span data-lake-id="u45aa2fcd" id="u45aa2fcd">String</span></code><span data-lake-id="u7fb38018" id="u7fb38018">。</span></p>
  <p data-lake-id="u58ba503d" id="u58ba503d"><br></p>
  <p data-lake-id="u897898b5" id="u897898b5"><span data-lake-id="ud1e92d00" id="ud1e92d00">在开始coding之前先科普下，Java中的</span><code data-lake-id="u03349be1" id="u03349be1"><span data-lake-id="uaa0c7fd7" id="uaa0c7fd7">switch</span></code><span data-lake-id="ufd65288c" id="ufd65288c">自身原本就支持基本类型。比如</span><code data-lake-id="udd29710b" id="udd29710b"><span data-lake-id="u992dec33" id="u992dec33">int</span></code><span data-lake-id="ucb33866a" id="ucb33866a">、</span><code data-lake-id="u48322cf6" id="u48322cf6"><span data-lake-id="u6a1c7b0d" id="u6a1c7b0d">char</span></code><span data-lake-id="u8ddb24e4" id="u8ddb24e4">等。对于</span><code data-lake-id="u4bc37a9f" id="u4bc37a9f"><span data-lake-id="ucdbde6e1" id="ucdbde6e1">int</span></code><span data-lake-id="ua176149e" id="ua176149e">类型，直接进行数值的比较。对于</span><code data-lake-id="u35815115" id="u35815115"><span data-lake-id="ua9cc218d" id="ua9cc218d">char</span></code><span data-lake-id="ucac8746b" id="ucac8746b">类型则是比较其ascii码。所以，对于编译器来说，</span><code data-lake-id="u14f23632" id="u14f23632"><span data-lake-id="udfd6f6f3" id="udfd6f6f3">switch</span></code><span data-lake-id="u0a4f9ddc" id="u0a4f9ddc">中其实只能使用整型，任何类型的比较都要转换成整型。比如</span><code data-lake-id="u4e16e50c" id="u4e16e50c"><span data-lake-id="ue2abe422" id="ue2abe422">byte</span></code><span data-lake-id="u31b6ea19" id="u31b6ea19">。</span><code data-lake-id="u36c58fdc" id="u36c58fdc"><span data-lake-id="u09f88c81" id="u09f88c81">short</span></code><span data-lake-id="u1119ce34" id="u1119ce34">，</span><code data-lake-id="u5e1bcf50" id="u5e1bcf50"><span data-lake-id="u2c1801d8" id="u2c1801d8">char</span></code><span data-lake-id="u1b3bfb5c" id="u1b3bfb5c">(asckii码是整型)以及</span><code data-lake-id="ub6587212" id="ub6587212"><span data-lake-id="ue9b9f7b7" id="ue9b9f7b7">int</span></code><span data-lake-id="u049b58e8" id="u049b58e8">。</span></p>
  <p data-lake-id="uf620c30a" id="uf620c30a"><br></p>
  <p data-lake-id="ubd0acce4" id="ubd0acce4"><span data-lake-id="uebe2d066" id="uebe2d066">那么接下来看下</span><code data-lake-id="u2e48b7b5" id="u2e48b7b5"><span data-lake-id="ue81d3471" id="ue81d3471">switch</span></code><span data-lake-id="ud2ff266a" id="ud2ff266a">对</span><code data-lake-id="u0529e11a" id="u0529e11a"><span data-lake-id="ud265e62b" id="ud265e62b">String</span></code><span data-lake-id="ue31cf93c" id="ue31cf93c">得支持，有以下代码：</span></p>
  <p data-lake-id="u10185e76" id="u10185e76"><br></p>
  <pre lang="java"><code>
public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
        case "hello":
            System.out.println("hello");
            break;
        case "world":
            System.out.println("world");
            break;
        default:
            break;
        }
    }
}
</code></pre>
  <p data-lake-id="u4b196f0b" id="u4b196f0b"><br></p>
  <p data-lake-id="u691bf75b" id="u691bf75b"><span data-lake-id="u7f704d6b" id="u7f704d6b">反编译后内容如下：</span></p>
  <p data-lake-id="u520dba43" id="u520dba43"><br></p>
  <pre lang="java"><code>
public class switchDemoString
{
    public switchDemoString()
    {
    }
    public static void main(String args[])
    {
        String str = "world";
        String s;
        switch((s = str).hashCode())
        {
        default:
            break;
        case 99162322:
            if(s.equals("hello"))
                System.out.println("hello");
            break;
        case 113318802:
            if(s.equals("world"))
                System.out.println("world");
            break;
        }
    }
}
</code></pre>
  <p data-lake-id="ucdd8f4dc" id="ucdd8f4dc"><br></p>
  <p data-lake-id="u62ec4dcc" id="u62ec4dcc"><span data-lake-id="u562a2df8" id="u562a2df8">看到这个代码，你知道原来</span><strong><span data-lake-id="uef0c8174" id="uef0c8174">字符串的switch是通过</span></strong><code data-lake-id="ub388eb79" id="ub388eb79"><strong><span data-lake-id="u73528746" id="u73528746">equals()</span></strong></code><strong><span data-lake-id="u8cc0fb12" id="u8cc0fb12">和</span></strong><code data-lake-id="u9a03f8b2" id="u9a03f8b2"><strong><span data-lake-id="u80a6a2d9" id="u80a6a2d9">hashCode()</span></strong></code><strong><span data-lake-id="u8432c5da" id="u8432c5da">方法来实现的。</span></strong><span data-lake-id="u092a036d" id="u092a036d">还好</span><code data-lake-id="u6e1e171c" id="u6e1e171c"><span data-lake-id="ufdd3f84c" id="ufdd3f84c">hashCode()</span></code><span data-lake-id="ub030e8da" id="ub030e8da">方法返回的是</span><code data-lake-id="u93472d95" id="u93472d95"><span data-lake-id="uf2f9c26b" id="uf2f9c26b">int</span></code><span data-lake-id="u50a3b677" id="u50a3b677">，而不是</span><code data-lake-id="u6f1e1fbc" id="u6f1e1fbc"><span data-lake-id="u1d1a4b26" id="u1d1a4b26">long</span></code><span data-lake-id="u08400b8e" id="u08400b8e">。</span></p>
  <p data-lake-id="u02123134" id="u02123134"><br></p>
  <blockquote data-lake-id="u1fc44990" id="u1fc44990">
   <p data-lake-id="uc9da5c23" id="uc9da5c23"><span data-lake-id="ua4413bab" id="ua4413bab">仔细看下可以发现，进行</span><code data-lake-id="u2a712906" id="u2a712906"><span data-lake-id="u728c37c3" id="u728c37c3">switch</span></code><span data-lake-id="u2af293b7" id="u2af293b7">的实际是哈希值，然后通过使用</span><code data-lake-id="ucd657e73" id="ucd657e73"><span data-lake-id="uaefc0c6d" id="uaefc0c6d">equals</span></code><span data-lake-id="u5d2346cc" id="u5d2346cc">方法比较进行安全检查，这个检查是必要的，因为哈希可能会发生碰撞。因此它的性能是不如使用枚举进行switch或者使用纯整数常量，但这也不是很差。</span></p>
  </blockquote>
  <p data-lake-id="uc2062d2b" id="uc2062d2b"><br></p>
  <h3 data-lake-id="77e41a31" id="77e41a31"><span data-lake-id="u885b3583" id="u885b3583">糖块二、 泛型</span></h3>
  <p data-lake-id="u97b172ed" id="u97b172ed"><br></p>
  <p data-lake-id="u4d2fcf1e" id="u4d2fcf1e"><span data-lake-id="ue4ce2984" id="ue4ce2984">我们都知道，很多语言都是支持泛型的，但是很多人不知道的是，不同的编译器对于泛型的处理方式是不同的，通常情况下，一个编译器处理泛型有两种方式：</span><code data-lake-id="u63dbf008" id="u63dbf008"><span data-lake-id="u72540408" id="u72540408">Code specialization</span></code><span data-lake-id="uf973c5a0" id="uf973c5a0">和</span><code data-lake-id="u17275e91" id="u17275e91"><span data-lake-id="u7fd1dcc4" id="u7fd1dcc4">Code sharing</span></code><span data-lake-id="u8464ebb3" id="u8464ebb3">。C++和C#是使用</span><code data-lake-id="u4c04b63e" id="u4c04b63e"><span data-lake-id="ub0b95358" id="ub0b95358">Code specialization</span></code><span data-lake-id="uba0c9673" id="uba0c9673">的处理机制，而Java使用的是</span><code data-lake-id="u447f4453" id="u447f4453"><span data-lake-id="u378218e3" id="u378218e3">Code sharing</span></code><span data-lake-id="ua3c9cf0c" id="ua3c9cf0c">的机制。</span></p>
  <p data-lake-id="u337451d2" id="u337451d2"><br></p>
  <blockquote data-lake-id="u2c67e588" id="u2c67e588">
   <p data-lake-id="u3383f1f2" id="u3383f1f2"><span data-lake-id="u03ed0bd7" id="u03ed0bd7">Code sharing方式为每个泛型类型创建唯一的字节码表示，并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除（</span><code data-lake-id="u83569955" id="u83569955"><span data-lake-id="u02eb435a" id="u02eb435a">type erasure</span></code><span data-lake-id="u48d32bf3" id="u48d32bf3">）实现的。</span></p>
  </blockquote>
  <p data-lake-id="u4f27ec2d" id="u4f27ec2d"><br></p>
  <p data-lake-id="u773e6282" id="u773e6282"><span data-lake-id="u5a17087e" id="u5a17087e">也就是说，</span><strong><span data-lake-id="u254f487e" id="u254f487e">对于Java虚拟机来说，他根本不认识</span></strong><code data-lake-id="u9e935b18" id="u9e935b18"><strong><span data-lake-id="ue1d05840" id="ue1d05840">Map&lt;String, String&gt; map</span></strong></code><strong><span data-lake-id="uadbe076b" id="uadbe076b">这样的语法。需要在编译阶段通过类型擦除的方式进行解语法糖。</span></strong></p>
  <p data-lake-id="uf959ddd2" id="uf959ddd2"><br></p>
  <p data-lake-id="u4676a3e3" id="u4676a3e3"><span data-lake-id="u22c6bcca" id="u22c6bcca">类型擦除的主要过程如下： 1.将所有的泛型参数用其最左边界（最顶级的父类型）类型替换。 2.移除所有的类型参数。</span></p>
  <p data-lake-id="uc573077c" id="uc573077c"><br></p>
  <p data-lake-id="u08cf7c81" id="u08cf7c81"><span data-lake-id="ue19058b7" id="ue19058b7">以下代码：</span></p>
  <p data-lake-id="u401cafbb" id="u401cafbb"><br></p>
  <pre lang="java"><code>
Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();  
map.put("name", "hollis");  
map.put("wechat", "Hollis");  
map.put("blog", "www.hollischuang.com");
</code></pre>
  <p data-lake-id="u57a2a115" id="u57a2a115"><br></p>
  <p data-lake-id="u43d2bfe0" id="u43d2bfe0"><span data-lake-id="u6eee35a8" id="u6eee35a8">解语法糖之后会变成：</span></p>
  <p data-lake-id="u720f7c1b" id="u720f7c1b"><br></p>
  <pre lang="java"><code>
Map map = new HashMap();  
map.put("name", "hollis");  
map.put("wechat", "Hollis");  
map.put("blog", "www.hollischuang.com");
</code></pre>
  <p data-lake-id="u7b3d099b" id="u7b3d099b"><br></p>
  <p data-lake-id="uf975fd28" id="uf975fd28"><span data-lake-id="ubca908ee" id="ubca908ee">以下代码：</span></p>
  <p data-lake-id="ue6c24bf7" id="ue6c24bf7"><br></p>
  <pre lang="java"><code>
public static &lt;A extends Comparable&lt;A&gt;&gt; A max(Collection&lt;A&gt; xs) {
    Iterator&lt;A&gt; xi = xs.iterator();
    A w = xi.next();
    while (xi.hasNext()) {
        A x = xi.next();
        if (w.compareTo(x) &lt; 0)
            w = x;
    }
    return w;
}
</code></pre>
  <p data-lake-id="u58f49583" id="u58f49583"><br></p>
  <p data-lake-id="ub9213cc9" id="ub9213cc9"><span data-lake-id="uf37bc599" id="uf37bc599">类型擦除后会变成：</span></p>
  <p data-lake-id="u21d96786" id="u21d96786"><br></p>
  <pre lang="java"><code>
 public static Comparable max(Collection xs){
    Iterator xi = xs.iterator();
    Comparable w = (Comparable)xi.next();
    while(xi.hasNext())
    {
        Comparable x = (Comparable)xi.next();
        if(w.compareTo(x) &lt; 0)
            w = x;
    }
    return w;
}
</code></pre>
  <p data-lake-id="ub61da7d2" id="ub61da7d2"><br></p>
  <p data-lake-id="ua73c1e27" id="ua73c1e27"><strong><span data-lake-id="u6e43e250" id="u6e43e250">虚拟机中没有泛型，只有普通类和普通方法，所有泛型类的类型参数在编译时都会被擦除，泛型类并没有自己独有的</span></strong><code data-lake-id="u6a36601f" id="u6a36601f"><strong><span data-lake-id="u4fd9aa64" id="u4fd9aa64">Class</span></strong></code><strong><span data-lake-id="u7d0bd684" id="u7d0bd684">类对象。比如并不存在</span></strong><code data-lake-id="u0f8f5135" id="u0f8f5135"><strong><span data-lake-id="u553c2943" id="u553c2943">List&lt;String&gt;.class</span></strong></code><strong><span data-lake-id="u9ae813b0" id="u9ae813b0">或是</span></strong><code data-lake-id="ubb564454" id="ubb564454"><strong><span data-lake-id="uc437188c" id="uc437188c">List&lt;Integer&gt;.class</span></strong></code><strong><span data-lake-id="udb778d9b" id="udb778d9b">，而只有</span></strong><code data-lake-id="ue1fb37ae" id="ue1fb37ae"><strong><span data-lake-id="uddd3362b" id="uddd3362b">List.class</span></strong></code><strong><span data-lake-id="u9c7d3be2" id="u9c7d3be2">。</span></strong></p>
  <p data-lake-id="u22a3237d" id="u22a3237d"><br></p>
  <h3 data-lake-id="a95a6741" id="a95a6741"><span data-lake-id="ued075586" id="ued075586">糖块三、 自动装箱与拆箱</span></h3>
  <p data-lake-id="u5093c5ba" id="u5093c5ba"><br></p>
  <p data-lake-id="u9fdbbd55" id="u9fdbbd55"><span data-lake-id="u40ad7ba8" id="u40ad7ba8">自动装箱就是Java自动将原始类型值转换成对应的对象，比如将int的变量转换成Integer对象，这个过程叫做装箱，反之将Integer对象转换成int类型值，这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换，所以就称作为自动装箱和拆箱。原始类型byte, short, char, int, long, float, double 和 boolean 对应的封装类为Byte, Short, Character, Integer, Long, Float, Double, Boolean。</span></p>
  <p data-lake-id="u5aae08e5" id="u5aae08e5"><br></p>
  <p data-lake-id="u66376804" id="u66376804"><span data-lake-id="u690caf08" id="u690caf08">先来看个自动装箱的代码：</span></p>
  <p data-lake-id="u1c4582f4" id="u1c4582f4"><br></p>
  <pre lang="java"><code>
 public static void main(String[] args) {
    int i = 10;
    Integer n = i;
}
</code></pre>
  <p data-lake-id="u34c49802" id="u34c49802"><br></p>
  <p data-lake-id="ucb5e6ba6" id="ucb5e6ba6"><span data-lake-id="ud102bb5f" id="ud102bb5f">反编译后代码如下:</span></p>
  <p data-lake-id="u84d64a23" id="u84d64a23"><br></p>
  <pre lang="java"><code>
public static void main(String args[])
{
    int i = 10;
    Integer n = Integer.valueOf(i);
}
</code></pre>
  <p data-lake-id="u47a2039a" id="u47a2039a"><br></p>
  <p data-lake-id="uf64ca3e7" id="uf64ca3e7"><span data-lake-id="u716300c8" id="u716300c8">再来看个自动拆箱的代码：</span></p>
  <p data-lake-id="u19b4514c" id="u19b4514c"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {

    Integer i = 10;
    int n = i;
}
</code></pre>
  <p data-lake-id="u1e93357a" id="u1e93357a"><br></p>
  <p data-lake-id="u171c1b09" id="u171c1b09"><span data-lake-id="ua8da375a" id="ua8da375a">反编译后代码如下：</span></p>
  <p data-lake-id="ucf52702e" id="ucf52702e"><br></p>
  <pre lang="java"><code>
public static void main(String args[])
{
    Integer i = Integer.valueOf(10);
    int n = i.intValue();
}
</code></pre>
  <p data-lake-id="u2f1029cd" id="u2f1029cd"><br></p>
  <p data-lake-id="u846852a7" id="u846852a7"><span data-lake-id="u391bca7f" id="u391bca7f">从反编译得到内容可以看出，在装箱的时候自动调用的是</span><code data-lake-id="u7fccc235" id="u7fccc235"><span data-lake-id="u108a3a6b" id="u108a3a6b">Integer</span></code><span data-lake-id="u2614ceff" id="u2614ceff">的</span><code data-lake-id="u27e373fb" id="u27e373fb"><span data-lake-id="u64e694b6" id="u64e694b6">valueOf(int)</span></code><span data-lake-id="ub9d849f7" id="ub9d849f7">方法。而在拆箱的时候自动调用的是</span><code data-lake-id="u810c6f37" id="u810c6f37"><span data-lake-id="u1b9098e9" id="u1b9098e9">Integer</span></code><span data-lake-id="uff47978f" id="uff47978f">的</span><code data-lake-id="u0e1c0f0b" id="u0e1c0f0b"><span data-lake-id="ua76d8230" id="ua76d8230">intValue</span></code><span data-lake-id="uee248e0e" id="uee248e0e">方法。</span></p>
  <p data-lake-id="uc3b2d2ad" id="uc3b2d2ad"><br></p>
  <p data-lake-id="u008dfc75" id="u008dfc75"><span data-lake-id="u300f7dc3" id="u300f7dc3">所以，</span><strong><span data-lake-id="u2b6a313f" id="u2b6a313f">装箱过程是通过调用包装器的valueOf方法实现的，而拆箱过程是通过调用包装器的 xxxValue方法实现的。</span></strong></p>
  <p data-lake-id="u413ef789" id="u413ef789"><br></p>
  <h3 data-lake-id="c8b67f3a" id="c8b67f3a"><span data-lake-id="ua808ad9d" id="ua808ad9d">糖块四 、 方法变长参数</span></h3>
  <p data-lake-id="u5e355305" id="u5e355305"><br></p>
  <p data-lake-id="u57390985" id="u57390985"><span data-lake-id="ufa16eeb1" id="ufa16eeb1">可变参数(</span><code data-lake-id="ubf2626f2" id="ubf2626f2"><span data-lake-id="u5855fc0f" id="u5855fc0f">variable arguments</span></code><span data-lake-id="u2c252a82" id="u2c252a82">)是在Java 1.5中引入的一个特性。它允许一个方法把任意数量的值作为参数。</span></p>
  <p data-lake-id="uaa52bee8" id="uaa52bee8"><br></p>
  <p data-lake-id="u90fdaa96" id="u90fdaa96"><span data-lake-id="u8979c071" id="u8979c071">看下以下可变参数代码，其中print方法接收可变参数：</span></p>
  <p data-lake-id="u5e8c582e" id="u5e8c582e"><br></p>
  <pre lang="java"><code>
public static void main(String[] args)
    {
        print("Holis", "公众号:Hollis", "博客：www.hollischuang.com", "QQ：907607222");
    }

public static void print(String... strs)
{
    for (int i = 0; i &lt; strs.length; i++)
    {
        System.out.println(strs[i]);
    }
}
</code></pre>
  <p data-lake-id="ubcfdf80d" id="ubcfdf80d"><br></p>
  <p data-lake-id="udf0323ad" id="udf0323ad"><span data-lake-id="uf1bb1e28" id="uf1bb1e28">反编译后代码：</span></p>
  <p data-lake-id="ue997e84b" id="ue997e84b"><br></p>
  <pre lang="java"><code>
 public static void main(String args[])
{
    print(new String[] {
        "Holis", "\u516C\u4F17\u53F7:Hollis", "\u535A\u5BA2\uFF1Awww.hollischuang.com", "QQ\uFF1A907607222"
    });
}

public static transient void print(String strs[])
{
    for(int i = 0; i &lt; strs.length; i++)
        System.out.println(strs[i]);

}
</code></pre>
  <p data-lake-id="uf3f3f783" id="uf3f3f783"><br></p>
  <p data-lake-id="uca056c2c" id="uca056c2c"><span data-lake-id="u15634935" id="u15634935">从反编译后代码可以看出，可变参数在被使用的时候，他首先会创建一个数组，数组的长度就是调用该方法是传递的实参的个数，然后再把参数值全部放到这个数组当中，然后再把这个数组作为参数传递到被调用的方法中。</span></p>
  <p data-lake-id="u22905035" id="u22905035"><br></p>
  <h3 data-lake-id="ed402c23" id="ed402c23"><span data-lake-id="u120e65de" id="u120e65de">糖块五 、 枚举</span></h3>
  <p data-lake-id="uaa0efd4c" id="uaa0efd4c"><br></p>
  <p data-lake-id="ub3ee62db" id="ub3ee62db"><span data-lake-id="u2da7350c" id="u2da7350c">在Java中，枚举是一种特殊的数据类型，用于表示有限的一组常量。枚举常量是在枚举类型中定义的，每个常量都是该类型的一个实例。Java中的枚举类型是一种安全而优雅的方式来表示有限的一组值。</span></p>
  <p data-lake-id="uf42957b0" id="uf42957b0"><br></p>
  <p data-lake-id="ucb2d075f" id="ucb2d075f"><span data-lake-id="uf2c2976b" id="uf2c2976b">要想看源码，首先得有一个类吧，那么枚举类型到底是什么类呢？是</span><code data-lake-id="uf137ba4b" id="uf137ba4b"><span data-lake-id="u0d8f59e4" id="u0d8f59e4">enum</span></code><span data-lake-id="u248251a6" id="u248251a6">吗？答案很明显不是，</span><code data-lake-id="uf7c6bae7" id="uf7c6bae7"><span data-lake-id="ub31072a4" id="ub31072a4">enum</span></code><span data-lake-id="u099c64e8" id="u099c64e8">就和</span><code data-lake-id="u99e4604f" id="u99e4604f"><span data-lake-id="ud078f2d7" id="ud078f2d7">class</span></code><span data-lake-id="u74bd2d90" id="u74bd2d90">一样，只是一个关键字，他并不是一个类，那么枚举是由什么类维护的呢，我们简单的写一个枚举：</span></p>
  <p data-lake-id="u08e7f5c8" id="u08e7f5c8"><br></p>
  <pre lang="java"><code>
public enum t {
    SPRING,SUMMER;
}
</code></pre>
  <p data-lake-id="u21c4b81a" id="u21c4b81a"><br></p>
  <p data-lake-id="ub04f6539" id="ub04f6539"><span data-lake-id="u7a683c2f" id="u7a683c2f">然后我们使用反编译，看看这段代码到底是怎么实现的，反编译后代码内容如下：</span></p>
  <p data-lake-id="u6656b71b" id="u6656b71b"><br></p>
  <pre lang="java"><code>
public final class T extends Enum
{
    private T(String s, int i)
    {
        super(s, i);
    }
    public static T[] values()
    {
        T at[];
        int i;
        T at1[];
        System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);
        return at1;
    }

    public static T valueOf(String s)
    {
        return (T)Enum.valueOf(demo/T, s);
    }

    public static final T SPRING;
    public static final T SUMMER;
    private static final T ENUM$VALUES[];
    static
    {
        SPRING = new T("SPRING", 0);
        SUMMER = new T("SUMMER", 1);
        ENUM$VALUES = (new T[] {
            SPRING, SUMMER
        });
    }
}
</code></pre>
  <p data-lake-id="uffc11034" id="uffc11034"><br></p>
  <p data-lake-id="u740fde1e" id="u740fde1e"><span data-lake-id="u897b5baf" id="u897b5baf">通过反编译后代码我们可以看到，</span><code data-lake-id="u1003c08b" id="u1003c08b"><span data-lake-id="u5ca1caa8" id="u5ca1caa8">public final class T extends Enum</span></code><span data-lake-id="u04af91b6" id="u04af91b6">，说明，该类是继承了</span><code data-lake-id="u343427e5" id="u343427e5"><span data-lake-id="u05a31a0e" id="u05a31a0e">Enum</span></code><span data-lake-id="uc6a55a69" id="uc6a55a69">类的，同时</span><code data-lake-id="uebf96374" id="uebf96374"><span data-lake-id="ueb282313" id="ueb282313">final</span></code><span data-lake-id="ucbaf02a3" id="ucbaf02a3">关键字告诉我们，这个类也是不能被继承的。</span><strong><span data-lake-id="ueecf3274" id="ueecf3274">当我们使用</span></strong><code data-lake-id="uf37540ad" id="uf37540ad"><strong><span data-lake-id="u3883fb8e" id="u3883fb8e">enum</span></strong></code><strong><span data-lake-id="uc6649ea7" id="uc6649ea7">来定义一个枚举类型的时候，编译器会自动帮我们创建一个</span></strong><code data-lake-id="u1a51106f" id="u1a51106f"><strong><span data-lake-id="u5f07597a" id="u5f07597a">final</span></strong></code><strong><span data-lake-id="ucc6e473d" id="ucc6e473d">类型的类继承</span></strong><code data-lake-id="uab59da8a" id="uab59da8a"><strong><span data-lake-id="uc3e7afdd" id="uc3e7afdd">Enum</span></strong></code><strong><span data-lake-id="u11862356" id="u11862356">类，所以枚举类型不能被继承。</span></strong></p>
  <p data-lake-id="uf297d59e" id="uf297d59e"><br></p>
  <h3 data-lake-id="af6ab5d6" id="af6ab5d6"><span data-lake-id="ud8faa9f9" id="ud8faa9f9">糖块六 、 内部类</span></h3>
  <p data-lake-id="u514f2032" id="u514f2032"><br></p>
  <p data-lake-id="u4206e9a3" id="u4206e9a3"><span data-lake-id="u5c9f60b1" id="u5c9f60b1">内部类又称为嵌套类，可以把内部类理解为外部类的一个普通成员。</span></p>
  <p data-lake-id="u5b64a246" id="u5b64a246"><br></p>
  <p data-lake-id="u0d2a6a0f" id="u0d2a6a0f"><strong><span data-lake-id="u5f21298d" id="u5f21298d">内部类之所以也是语法糖，是因为它仅仅是一个编译时的概念，</span></strong><code data-lake-id="uc5cadcea" id="uc5cadcea"><strong><span data-lake-id="u5f9a1864" id="u5f9a1864">outer.java</span></strong></code><strong><span data-lake-id="u09855f88" id="u09855f88">里面定义了一个内部类</span></strong><code data-lake-id="u305c1283" id="u305c1283"><strong><span data-lake-id="u9dd96c11" id="u9dd96c11">inner</span></strong></code><strong><span data-lake-id="u59f3c4cc" id="u59f3c4cc">，一旦编译成功，就会生成两个完全不同的</span></strong><code data-lake-id="ua5a945e1" id="ua5a945e1"><strong><span data-lake-id="u0259e331" id="u0259e331">.class</span></strong></code><strong><span data-lake-id="u82e4b08e" id="u82e4b08e">文件了，分别是</span></strong><code data-lake-id="u291223c8" id="u291223c8"><strong><span data-lake-id="u4570ca29" id="u4570ca29">outer.class</span></strong></code><strong><span data-lake-id="ufd16a19b" id="ufd16a19b">和</span></strong><code data-lake-id="u3bbc59e3" id="u3bbc59e3"><strong><span data-lake-id="ub175c0b1" id="ub175c0b1">outer$inner.class</span></strong></code><strong><span data-lake-id="uad611f0a" id="uad611f0a">。</span></strong></p>
  <p data-lake-id="u83714e94" id="u83714e94"><br></p>
  <pre lang="java"><code>
public class OutterClass {
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public static void main(String[] args) {

    }

    class InnerClass{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
</code></pre>
  <p data-lake-id="u1ab10e5a" id="u1ab10e5a"><br></p>
  <p data-lake-id="u20b1152e" id="u20b1152e"><span data-lake-id="u3f07c30a" id="u3f07c30a">以上代码编译后会生成两个class文件：</span><code data-lake-id="ued907144" id="ued907144"><span data-lake-id="u7f468189" id="u7f468189">OutterClass$InnerClass.class</span></code><span data-lake-id="u3e0104fb" id="u3e0104fb"> 、</span><code data-lake-id="u93ad3a0f" id="u93ad3a0f"><span data-lake-id="ud3abbdd0" id="ud3abbdd0">OutterClass.class</span></code><span data-lake-id="ua04618ec" id="ua04618ec"> 。当我们尝试对</span><code data-lake-id="u1a44ad5c" id="u1a44ad5c"><span data-lake-id="u9d232ac1" id="u9d232ac1">OutterClass.class</span></code><span data-lake-id="uc47064fb" id="uc47064fb">文件进行反编译的时候，命令行会打印以下内容：</span><code data-lake-id="uf611f0f5" id="uf611f0f5"><span data-lake-id="udabfd9ab" id="udabfd9ab">Parsing OutterClass.class...Parsing inner class OutterClass$InnerClass.class... Generating OutterClass.jad</span></code><span data-lake-id="u85c6247f" id="u85c6247f"> 。他会把两个文件全部进行反编译，然后一起生成一个</span><code data-lake-id="u4004cd7c" id="u4004cd7c"><span data-lake-id="u945935a3" id="u945935a3">OutterClass.jad</span></code><span data-lake-id="u635beabb" id="u635beabb">文件。文件内容如下：</span></p>
  <p data-lake-id="u68e12d3a" id="u68e12d3a"><br></p>
  <pre lang="java"><code>
public class OutterClass
{
    class InnerClass
    {
        public String getName()
        {
            return name;
        }
        public void setName(String name)
        {
            this.name = name;
        }
        private String name;
        final OutterClass this$0;

        InnerClass()
        {
            this.this$0 = OutterClass.this;
            super();
        }
    }

    public OutterClass()
    {
    }
    public String getUserName()
    {
        return userName;
    }
    public void setUserName(String userName){
        this.userName = userName;
    }
    public static void main(String args1[])
    {
    }
    private String userName;
}
</code></pre>
  <p data-lake-id="u96478c8b" id="u96478c8b"><br></p>
  <h3 data-lake-id="261055e8" id="261055e8"><span data-lake-id="uf5a6aa9a" id="uf5a6aa9a">糖块七 、条件编译</span></h3>
  <p data-lake-id="u459e0156" id="u459e0156"><br></p>
  <p data-lake-id="ud407ea88" id="ud407ea88"><span data-lake-id="u830f758d" id="u830f758d">—般情况下，程序中的每一行代码都要参加编译。但有时候出于对程序代码优化的考虑，希望只对其中一部分内容进行编译，此时就需要在程序中加上条件，让编译器只对满足条件的代码进行编译，将不满足条件的代码舍弃，这就是条件编译。</span></p>
  <p data-lake-id="ud0531d80" id="ud0531d80"><br></p>
  <p data-lake-id="u94e2b1dd" id="u94e2b1dd"><span data-lake-id="u3b17fb49" id="u3b17fb49">如在C或CPP中，可以通过预处理语句来实现条件编译。其实在Java中也可实现条件编译。我们先来看一段代码：</span></p>
  <p data-lake-id="u0681673a" id="u0681673a"><br></p>
  <pre lang="java"><code>
public class ConditionalCompilation {
    public static void main(String[] args) {
        final boolean DEBUG = true;
        if(DEBUG) {
            System.out.println("Hello, DEBUG!");
        }

        final boolean ONLINE = false;

        if(ONLINE){
            System.out.println("Hello, ONLINE!");
        }
    }
}
</code></pre>
  <p data-lake-id="ucd66c381" id="ucd66c381"><br></p>
  <p data-lake-id="ucd4b6e22" id="ucd4b6e22"><span data-lake-id="ub4f341d7" id="ub4f341d7">反编译后代码如下：</span></p>
  <p data-lake-id="u11b043bc" id="u11b043bc"><br></p>
  <pre lang="java"><code>
public class ConditionalCompilation
{

    public ConditionalCompilation()
    {
    }

    public static void main(String args[])
    {
        boolean DEBUG = true;
        System.out.println("Hello, DEBUG!");
        boolean ONLINE = false;
    }
}
</code></pre>
  <p data-lake-id="u240eea4d" id="u240eea4d"><br></p>
  <p data-lake-id="u0603a6ed" id="u0603a6ed"><span data-lake-id="u66cb2646" id="u66cb2646">首先，我们发现，在反编译后的代码中没有</span><code data-lake-id="u839c81ae" id="u839c81ae"><span data-lake-id="ue354c4e6" id="ue354c4e6">System.out.println("Hello, ONLINE!");</span></code><span data-lake-id="u70a07ae7" id="u70a07ae7">，这其实就是条件编译。当</span><code data-lake-id="u04965ab6" id="u04965ab6"><span data-lake-id="u4894deb1" id="u4894deb1">if(ONLINE)</span></code><span data-lake-id="u8b160a7b" id="u8b160a7b">为false的时候，编译器就没有对其内的代码进行编译。</span></p>
  <p data-lake-id="u4187ff46" id="u4187ff46"><br></p>
  <p data-lake-id="u8aaed054" id="u8aaed054"><span data-lake-id="u7e5506ce" id="u7e5506ce">所以，</span><strong><span data-lake-id="u604b041d" id="u604b041d">Java语法的条件编译，是通过判断条件为常量的if语句实现的。其原理也是Java语言的语法糖。根据if判断条件的真假，编译器直接把分支为false的代码块消除。通过该方式实现的条件编译，必须在方法体内实现，而无法在正整个Java类的结构或者类的属性上进行条件编译，这与C/C++的条件编译相比，确实更有局限性。在Java语言设计之初并没有引入条件编译的功能，虽有局限，但是总比没有更强。</span></strong></p>
  <p data-lake-id="ue2b05a3f" id="ue2b05a3f"><br></p>
  <h3 data-lake-id="ef8aeeed" id="ef8aeeed"><span data-lake-id="u026c766f" id="u026c766f">糖块八 、 断言</span></h3>
  <p data-lake-id="ub4ae39f9" id="ub4ae39f9"><br></p>
  <p data-lake-id="u19edaf23" id="u19edaf23"><span data-lake-id="u525210cf" id="u525210cf">在Java中，</span><code data-lake-id="ub829bc27" id="ub829bc27"><span data-lake-id="u6d6c9ec0" id="u6d6c9ec0">assert</span></code><span data-lake-id="ufe7693e1" id="ufe7693e1">关键字是从JAVA SE 1.4 引入的，为了避免和老版本的Java代码中使用了</span><code data-lake-id="u2c784e5b" id="u2c784e5b"><span data-lake-id="u831b7385" id="u831b7385">assert</span></code><span data-lake-id="ud92a5f4c" id="ud92a5f4c">关键字导致错误，Java在执行的时候默认是不启动断言检查的（这个时候，所有的断言语句都将忽略！），如果要开启断言检查，则需要用开关</span><code data-lake-id="u3723be32" id="u3723be32"><span data-lake-id="ufcb71d5e" id="ufcb71d5e">-enableassertions</span></code><span data-lake-id="u9dae6081" id="u9dae6081">或</span><code data-lake-id="uf876f752" id="uf876f752"><span data-lake-id="u4faffdfc" id="u4faffdfc">-ea</span></code><span data-lake-id="ud53213de" id="ud53213de">来开启。</span></p>
  <p data-lake-id="u566c21ce" id="u566c21ce"><br></p>
  <p data-lake-id="u0ff59330" id="u0ff59330"><span data-lake-id="u343a239a" id="u343a239a">看一段包含断言的代码：</span></p>
  <p data-lake-id="u9492b32c" id="u9492b32c"><br></p>
  <pre lang="java"><code>
public class AssertTest {
    public static void main(String args[]) {
        int a = 1;
        int b = 1;
        assert a == b;
        System.out.println("公众号：Hollis");
        assert a != b : "Hollis";
        System.out.println("博客：www.hollischuang.com");
    }
}
</code></pre>
  <p data-lake-id="u341884ed" id="u341884ed"><br></p>
  <p data-lake-id="ucc6fb730" id="ucc6fb730"><span data-lake-id="u68eb5b58" id="u68eb5b58">反编译后代码如下：</span></p>
  <p data-lake-id="u07e2903c" id="u07e2903c"><br></p>
  <pre lang="java"><code>
public class AssertTest {
   public AssertTest()
    {
    }
    public static void main(String args[])
{
    int a = 1;
    int b = 1;
    if(!$assertionsDisabled &amp;&amp; a != b)
        throw new AssertionError();
    System.out.println("\u516C\u4F17\u53F7\uFF1AHollis");
    if(!$assertionsDisabled &amp;&amp; a == b)
    {
        throw new AssertionError("Hollis");
    } else
    {
        System.out.println("\u535A\u5BA2\uFF1Awww.hollischuang.com");
        return;
    }
}

static final boolean $assertionsDisabled = !com/hollis/suguar/AssertTest.desiredAssertionStatus();


}
</code></pre>
  <p data-lake-id="u4264def9" id="u4264def9"><br></p>
  <p data-lake-id="u461bd6c2" id="u461bd6c2"><span data-lake-id="u11168b41" id="u11168b41">很明显，反编译之后的代码要比我们自己的代码复杂的多。所以，使用了assert这个语法糖我们节省了很多代码。</span><strong><span data-lake-id="u7c818dd6" id="u7c818dd6">其实断言的底层实现就是if语言，如果断言结果为true，则什么都不做，程序继续执行，如果断言结果为false，则程序抛出AssertError来打断程序的执行。</span></strong><code data-lake-id="uffeda4f6" id="uffeda4f6"><span data-lake-id="ub1592b63" id="ub1592b63">-enableassertions</span></code><span data-lake-id="u1cf194be" id="u1cf194be">会设置$assertionsDisabled字段的值。</span></p>
  <p data-lake-id="u70abecb3" id="u70abecb3"><br></p>
  <h3 data-lake-id="0cbaa366" id="0cbaa366"><span data-lake-id="uc8665164" id="uc8665164">糖块九 、 数值字面量</span></h3>
  <p data-lake-id="u9e3ea32e" id="u9e3ea32e"><br></p>
  <p data-lake-id="uc3518b43" id="uc3518b43"><span data-lake-id="u7064dba9" id="u7064dba9">在java 7中，数值字面量，不管是整数还是浮点数，都允许在数字之间插入任意多个下划线。这些下划线不会对字面量的数值产生影响，目的就是方便阅读。</span></p>
  <p data-lake-id="u2c76c639" id="u2c76c639"><br></p>
  <p data-lake-id="u2ce4473b" id="u2ce4473b"><span data-lake-id="ua662d417" id="ua662d417">比如：</span></p>
  <p data-lake-id="ude81412d" id="ude81412d"><br></p>
  <pre lang="java"><code>
public class Test {
    public static void main(String... args) {
        int i = 10_000;
        System.out.println(i);
    }
}
</code></pre>
  <p data-lake-id="uebb3e045" id="uebb3e045"><br></p>
  <p data-lake-id="ubcb24073" id="ubcb24073"><span data-lake-id="u446768c9" id="u446768c9">反编译后：</span></p>
  <p data-lake-id="ubd618a9e" id="ubd618a9e"><br></p>
  <pre lang="java"><code>
public class Test
{
  public static void main(String[] args)
  {
    int i = 10000;
    System.out.println(i);
  }
}
</code></pre>
  <p data-lake-id="u09957975" id="u09957975"><br></p>
  <p data-lake-id="uc3dcbd94" id="uc3dcbd94"><span data-lake-id="u80fcdaa3" id="u80fcdaa3">反编译后就是把</span><code data-lake-id="u9041937d" id="u9041937d"><span data-lake-id="u078f2bc9" id="u078f2bc9">_</span></code><span data-lake-id="u4b66ae62" id="u4b66ae62">删除了。也就是说 </span><strong><span data-lake-id="u52856fc3" id="u52856fc3">编译器并不认识在数字字面量中的</span></strong><code data-lake-id="uf5ee1822" id="uf5ee1822"><strong><span data-lake-id="u00f1b7b7" id="u00f1b7b7">_</span></strong></code><strong><span data-lake-id="u34686d90" id="u34686d90">，需要在编译阶段把他去掉。</span></strong></p>
  <p data-lake-id="u1aea21e7" id="u1aea21e7"><br></p>
  <h3 data-lake-id="83d0bc62" id="83d0bc62"><span data-lake-id="u8ed1c440" id="u8ed1c440">糖块十 、 for-each</span></h3>
  <p data-lake-id="ue3634977" id="ue3634977"><br></p>
  <p data-lake-id="u1d27b422" id="u1d27b422"><span data-lake-id="uac6355c9" id="uac6355c9">增强for循环（</span><code data-lake-id="u15c2c672" id="u15c2c672"><span data-lake-id="u6c2bd56d" id="u6c2bd56d">for-each</span></code><span data-lake-id="u8cc907f8" id="u8cc907f8">）相信大家都不陌生，日常开发经常会用到的，他会比for循环要少写很多代码，那么这个语法糖背后是如何实现的呢？</span></p>
  <p data-lake-id="uf4618e7e" id="uf4618e7e"><br></p>
  <pre lang="java"><code>
public static void main(String... args) {
    String[] strs = {"Hollis", "公众号：Hollis", "博客：www.hollischuang.com"};
    for (String s : strs) {
        System.out.println(s);
    }
    List&lt;String&gt; strList = ImmutableList.of("Hollis", "公众号：Hollis", "博客：www.hollischuang.com");
    for (String s : strList) {
        System.out.println(s);
    }
}
</code></pre>
  <p data-lake-id="ua8e02a30" id="ua8e02a30"><br></p>
  <p data-lake-id="u8019c545" id="u8019c545"><span data-lake-id="u913309b6" id="u913309b6">反编译后代码如下：</span></p>
  <p data-lake-id="u81daf31a" id="u81daf31a"><br></p>
  <pre lang="java"><code>
public static transient void main(String args[])
{
    String strs[] = {
        "Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u535A\u5BA2\uFF1Awww.hollischuang.com"
    };
    String args1[] = strs;
    int i = args1.length;
    for(int j = 0; j &lt; i; j++)
    {
        String s = args1[j];
        System.out.println(s);
    }

    List strList = ImmutableList.of("Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u535A\u5BA2\uFF1Awww.hollischuang.com");
    String s;
    for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s))
        s = (String)iterator.next();

}
</code></pre>
  <p data-lake-id="ufd5fa21f" id="ufd5fa21f"><br></p>
  <p data-lake-id="u3d004089" id="u3d004089"><span data-lake-id="u4d46aa2e" id="u4d46aa2e">代码很简单，</span><strong><span data-lake-id="uf31bbf9d" id="uf31bbf9d">for-each的实现原理其实就是使用了普通的for循环和迭代器。</span></strong></p>
  <p data-lake-id="u7c5e961d" id="u7c5e961d"><br></p>
  <h3 data-lake-id="c4e078ee" id="c4e078ee"><span data-lake-id="u8686bcd5" id="u8686bcd5">糖块十一 、 try-with-resource</span></h3>
  <p data-lake-id="u875f2fb5" id="u875f2fb5"><br></p>
  <p data-lake-id="u09d8920f" id="u09d8920f"><span data-lake-id="u1fb5c0b7" id="u1fb5c0b7">Java里，对于文件操作IO流、数据库连接等开销非常昂贵的资源，用完之后必须及时通过close方法将其关闭，否则资源会一直处于打开状态，可能会导致内存泄露等问题。</span></p>
  <p data-lake-id="u7761b617" id="u7761b617"><br></p>
  <p data-lake-id="u40d782dc" id="u40d782dc"><span data-lake-id="u80f50ca6" id="u80f50ca6">关闭资源的常用方式就是在</span><code data-lake-id="ue486ecc3" id="ue486ecc3"><span data-lake-id="u53d50077" id="u53d50077">finally</span></code><span data-lake-id="u028e1f7e" id="u028e1f7e">块里是释放，即调用</span><code data-lake-id="ub67cb490" id="ub67cb490"><span data-lake-id="u2a5271d6" id="u2a5271d6">close</span></code><span data-lake-id="ud564282b" id="ud564282b">方法。比如，我们经常会写这样的代码：</span></p>
  <p data-lake-id="ud2a6bebc" id="ud2a6bebc"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\hollischuang.xml"));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    } finally {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException ex) {
            // handle exception
        }
    }
}
</code></pre>
  <p data-lake-id="u0dfff389" id="u0dfff389"><br></p>
  <p data-lake-id="u15c07278" id="u15c07278"><span data-lake-id="u975b42e5" id="u975b42e5">从Java 7开始，jdk提供了一种更好的方式关闭资源，使用</span><code data-lake-id="ud5eda5f7" id="ud5eda5f7"><span data-lake-id="u8b238daf" id="u8b238daf">try-with-resources</span></code><span data-lake-id="uecfd1e91" id="uecfd1e91">语句，改写一下上面的代码，效果如下：</span></p>
  <p data-lake-id="u3c8a4ea0" id="u3c8a4ea0"><br></p>
  <pre lang="java"><code>
public static void main(String... args) {
    try (BufferedReader br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    }
}
</code></pre>
  <p data-lake-id="uefbc3eda" id="uefbc3eda"><br></p>
  <p data-lake-id="ud95d1a45" id="ud95d1a45"><span data-lake-id="u5fe63444" id="u5fe63444">看，这简直是一大福音啊，虽然我之前一般使用</span><code data-lake-id="u52e6bee3" id="u52e6bee3"><span data-lake-id="ubdfd838a" id="ubdfd838a">IOUtils</span></code><span data-lake-id="u60861225" id="u60861225">去关闭流，并不会使用在</span><code data-lake-id="u0d975bad" id="u0d975bad"><span data-lake-id="u811761c8" id="u811761c8">finally</span></code><span data-lake-id="ueb5f177b" id="ueb5f177b">中写很多代码的方式，但是这种新的语法糖看上去好像优雅很多呢。看下他的背后：</span></p>
  <p data-lake-id="uf60c12d6" id="uf60c12d6"><br></p>
  <pre lang="java"><code>
public static transient void main(String args[])
    {
        BufferedReader br;
        Throwable throwable;
        br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"));
        throwable = null;
        String line;
        try
        {
            while((line = br.readLine()) != null)
                System.out.println(line);
        }
        catch(Throwable throwable2)
        {
            throwable = throwable2;
            throw throwable2;
        }
        if(br != null)
            if(throwable != null)
                try
                {
                    br.close();
                }
                catch(Throwable throwable1)
                {
                    throwable.addSuppressed(throwable1);
                }
            else
                br.close();
            break MISSING_BLOCK_LABEL_113;
            Exception exception;
            exception;
            if(br != null)
                if(throwable != null)
                    try
                    {
                        br.close();
                    }
                    catch(Throwable throwable3)
                      {
                        throwable.addSuppressed(throwable3);
                    }
                else
                    br.close();
        throw exception;
        IOException ioexception;
        ioexception;
    }
}
</code></pre>
  <p data-lake-id="u9b407d6d" id="u9b407d6d"><br></p>
  <p data-lake-id="ue926054b" id="ue926054b"><strong><span data-lake-id="u869b4b18" id="u869b4b18">其实背后的原理也很简单，那些我们没有做的关闭资源的操作，编译器都帮我们做了。所以，再次印证了，语法糖的作用就是方便程序员的使用，但最终还是要转成编译器认识的语言。</span></strong></p>
  <p data-lake-id="uc4f5e6eb" id="uc4f5e6eb"><br></p>
  <h3 data-lake-id="e6f23c9d" id="e6f23c9d"><span data-lake-id="ucfbbeeaf" id="ucfbbeeaf">糖块十二、Lambda表达式</span></h3>
  <p data-lake-id="ue6da1729" id="ue6da1729"><br></p>
  <p data-lake-id="u7bc019c5" id="u7bc019c5"><br></p>
  <h3 data-lake-id="025207d8" id="025207d8"><span data-lake-id="ue9e346ba" id="ue9e346ba">可能遇到的坑</span></h3>
  <p data-lake-id="u8864f729" id="u8864f729"><br></p>
  <h4 data-lake-id="8045759e" id="8045759e"><span data-lake-id="ua9b68f09" id="ua9b68f09">泛型</span></h4>
  <p data-lake-id="u93fc1c78" id="u93fc1c78"><br></p>
  <p data-lake-id="u28a88795" id="u28a88795"><strong><span data-lake-id="ufe51e68c" id="ufe51e68c">一、当泛型遇到重载</span></strong><span data-lake-id="uc2570fec" id="uc2570fec"> </span></p>
  <p data-lake-id="ub4e8ca92" id="ub4e8ca92"><br></p>
  <pre lang="java"><code>
 public class GenericTypes {
    public static void method(List&lt;String&gt; list) {  
        System.out.println("invoke method(List&lt;String&gt; list)");  
    }  

    public static void method(List&lt;Integer&gt; list) {  
        System.out.println("invoke method(List&lt;Integer&gt; list)");  
    }  
}
</code></pre>
  <p data-lake-id="u4029dc20" id="u4029dc20"><br></p>
  <p data-lake-id="ua93c247e" id="ua93c247e"><span data-lake-id="u3afcac27" id="u3afcac27">上面这段代码，有两个重载的函数，因为他们的参数类型不同，一个是List&lt;String&gt; 另一个是List&lt;Integer&gt; ，但是，这段代码是编译通不过的。因为我们前面讲过，参数List&lt;String&gt;和List&lt;Integer&gt;编译之后都被擦除了，变成了一样的原生类型List，擦除动作导致这两个方法的特征签名变得一模一样。</span></p>
  <p data-lake-id="u4028cf24" id="u4028cf24"><br></p>
  <p data-lake-id="u07ac22af" id="u07ac22af"><strong><span data-lake-id="u80db123e" id="u80db123e">二、当泛型遇到catch</span></strong><span data-lake-id="uc7de0c15" id="uc7de0c15"> 泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除，JVM是无法区分两个异常类型</span><code data-lake-id="u88bf43d0" id="u88bf43d0"><span data-lake-id="u6fc69a8d" id="u6fc69a8d">MyException&lt;String&gt;</span></code><span data-lake-id="ua13147e7" id="ua13147e7">和</span><code data-lake-id="uda5fa068" id="uda5fa068"><span data-lake-id="uc9aca605" id="uc9aca605">MyException&lt;Integer&gt;</span></code><span data-lake-id="u70219c7d" id="u70219c7d">的</span></p>
  <p data-lake-id="udf6f97ab" id="udf6f97ab"><br></p>
  <p data-lake-id="u42a08e22" id="u42a08e22"><strong><span data-lake-id="u7e858c99" id="u7e858c99">三、当泛型内包含静态变量</span></strong></p>
  <p data-lake-id="u0c52bc32" id="u0c52bc32"><br></p>
  <pre lang="java"><code>
public class StaticTest{
    public static void main(String[] args){
        GT&lt;Integer&gt; gti = new GT&lt;Integer&gt;();
        gti.var=1;
        GT&lt;String&gt; gts = new GT&lt;String&gt;();
        gts.var=2;
        System.out.println(gti.var);
    }
}
class GT&lt;T&gt;{
    public static int var=0;
    public void nothing(T x){}
}
</code></pre>
  <p data-lake-id="ue72fb3ee" id="ue72fb3ee"><br></p>
  <p data-lake-id="ufc52c9b4" id="ufc52c9b4"><span data-lake-id="u98661781" id="u98661781">以上代码输出结果为：2！由于经过类型擦除，所有的泛型类实例都关联到同一份字节码上，泛型类的所有静态变量是共享的。</span></p>
  <p data-lake-id="uf8aa81fa" id="uf8aa81fa"><br></p>
  <h4 data-lake-id="a809ab12" id="a809ab12"><span data-lake-id="u26973c1d" id="u26973c1d">自动装箱与拆箱</span></h4>
  <p data-lake-id="u1a5fe279" id="u1a5fe279"><br></p>
  <p data-lake-id="u83e354e7" id="u83e354e7"><strong><span data-lake-id="u92b29ad7" id="u92b29ad7">对象相等比较</span></strong></p>
  <p data-lake-id="u5e8b50a7" id="u5e8b50a7"><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    Integer a = 1000;
    Integer b = 1000;
    Integer c = 100;
    Integer d = 100;
    System.out.println("a == b is " + (a == b));
    System.out.println(("c == d is " + (c == d)));
}
</code></pre>
  <p data-lake-id="u4e9f0b26" id="u4e9f0b26"><br></p>
  <p data-lake-id="u6e2e079b" id="u6e2e079b"><span data-lake-id="u9bf0b5e4" id="u9bf0b5e4">输出结果：</span></p>
  <p data-lake-id="u122b4cc8" id="u122b4cc8"><br></p>
  <pre lang="java"><code>
a == b is false
c == d is true
</code></pre>
  <p data-lake-id="uafa6953d" id="uafa6953d"><br></p>
  <p data-lake-id="ucc8ca6e9" id="ucc8ca6e9"><span data-lake-id="u15119ab7" id="u15119ab7">在Java 5中，在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。</span></p>
  <p data-lake-id="uf69148de" id="uf69148de"><br></p>
  <blockquote data-lake-id="u12c96acf" id="u12c96acf">
   <p data-lake-id="u5a17f859" id="u5a17f859"><span data-lake-id="ucd3542f3" id="ucd3542f3">适用于整数值区间-128 至 +127。</span></p>
   <p data-lake-id="ua0f9026a" id="ua0f9026a"><span data-lake-id="u61c11a07" id="u61c11a07"> </span></p>
   <p data-lake-id="u37f40c19" id="u37f40c19"><span data-lake-id="u7f777373" id="u7f777373">只适用于自动装箱。使用构造函数创建对象不适用。</span></p>
  </blockquote>
  <p data-lake-id="u460084cb" id="u460084cb"><br></p>
  <h4 data-lake-id="11cfc387" id="11cfc387"><span data-lake-id="u0e0841ef" id="u0e0841ef">增强for循环</span></h4>
  <p data-lake-id="uc124cf9a" id="uc124cf9a"><br></p>
  <p data-lake-id="uc3296f18" id="uc3296f18"><strong><span data-lake-id="ua0e4d835" id="ua0e4d835">ConcurrentModificationException</span></strong></p>
  <p data-lake-id="u3900b872" id="u3900b872"><br></p>
  <pre lang="java"><code>
for (Student stu : students) {    
    if (stu.getId() == 2)     
        students.remove(stu);    
}
</code></pre>
  <p data-lake-id="ue50b5f99" id="ue50b5f99"><br></p>
  <p data-lake-id="u40e3854a" id="u40e3854a"><span data-lake-id="uee555bc1" id="uee555bc1">会抛出</span><code data-lake-id="ud22fd11f" id="ud22fd11f"><span data-lake-id="uca18bb5f" id="uca18bb5f">ConcurrentModificationException</span></code><span data-lake-id="ua88dbdb5" id="ua88dbdb5">异常。</span></p>
  <p data-lake-id="uccf32e00" id="uccf32e00"><br></p>
  <p data-lake-id="u713e87cc" id="u713e87cc"><span data-lake-id="u23f319b1" id="u23f319b1">Iterator是工作在一个独立的线程中，并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表，当原来的对象数量发生变化时，这个索引表的内容不会同步改变，所以当索引指针往后移动的时候就找不到要迭代的对象，所以按照 fail-fast 原则 Iterator 会马上抛出</span><code data-lake-id="uaa003709" id="uaa003709"><span data-lake-id="u8580ea52" id="u8580ea52">java.util.ConcurrentModificationException</span></code><span data-lake-id="u9930c11f" id="u9930c11f">异常。</span></p>
  <p data-lake-id="ubf15d095" id="ubf15d095"><br></p>
  <p data-lake-id="u174dabb7" id="u174dabb7"><span data-lake-id="ue58adec7" id="ue58adec7">所以 </span><code data-lake-id="ufab49a0f" id="ufab49a0f"><span data-lake-id="ue77965b9" id="ue77965b9">Iterator</span></code><span data-lake-id="u2788caa0" id="u2788caa0"> 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 </span><code data-lake-id="u283a2e07" id="u283a2e07"><span data-lake-id="uca6638eb" id="uca6638eb">Iterator</span></code><span data-lake-id="u83cf8b46" id="u83cf8b46"> 本身的方法</span><code data-lake-id="u9ba9d571" id="u9ba9d571"><span data-lake-id="u05baf6b3" id="u05baf6b3">remove()</span></code><span data-lake-id="u381abf2e" id="u381abf2e">来删除对象，</span><code data-lake-id="uf603bd14" id="uf603bd14"><span data-lake-id="ubac3f704" id="ubac3f704">Iterator.remove()</span></code><span data-lake-id="u886a7b4a" id="u886a7b4a"> 方法会在删除当前迭代对象的同时维护索引的一致性。</span></p>
  <p data-lake-id="u400183f0" id="u400183f0"><br></p>
  <h3 data-lake-id="25f9c7fa" id="25f9c7fa"><span data-lake-id="ua1917002" id="ua1917002">总结</span></h3>
  <p data-lake-id="u9d3640bb" id="u9d3640bb"><br></p>
  <p data-lake-id="u765bd56f" id="u765bd56f"><span data-lake-id="ua3e99745" id="ua3e99745">前面介绍了12种Java中常用的语法糖。所谓语法糖就是提供给开发人员便于开发的一种语法而已。但是这种语法只有开发人员认识。要想被执行，需要进行解糖，即转成JVM认识的语法。当我们把语法糖解糖之后，你就会发现其实我们日常使用的这些方便的语法，其实都是一些其他更简单的语法构成的。</span></p>
  <p data-lake-id="ue8e97670" id="ue8e97670"><br></p>
  <p data-lake-id="uaa855acc" id="uaa855acc"><span data-lake-id="u44cace72" id="u44cace72">有了这些语法糖，我们在日常开发的时候可以大大提升效率，但是同时也要避免过渡使用。使用之前最好了解下原理，避免掉坑。</span></p>
 </body>
</html>